﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Google.GData.Client;
using Google.GData.Extensions;
using Google.GData.YouTube;
using Google.GData.Extensions.MediaRss;
using Google.YouTube;

using System.Collections;
using System.Diagnostics;

namespace YouTubeAccessLayer
{
    public class YouTubeFunctions
    {
        private String sSource = "YouTube Functions";
        private String sLog = "Application";
        
        private YouTubeRequestSettings settings;
        private YouTubeRequest request = null;
        private String videoId = null;
        private Video latestVideo = null;

        public String VideoId
        {
            get { return videoId; }

            set { videoId = value; }
        }

        public YouTubeFunctions(string developerKey, string accountName, string username, string password)
        {
            // this setting will access all the videos uploaded by user.
            settings = new YouTubeRequestSettings(accountName, developerKey, username, password);
            // this will access only the public video feed for the user
            //YouTubeRequestSettings settings = new YouTubeRequestSettings("testcgnetswara", developerKey);

            request = new YouTubeRequest(settings);

            // setting up the event logger
            if (!EventLog.SourceExists(sSource))
                EventLog.CreateEventSource(sSource, sLog);
            EventLog.WriteEntry(sSource, "Constructor completed");

        }

        public int UploadVideoOnYouTube(String videoFileLocation, String title, String keywords, 
            String description, String location)
        {
            int videoUploadStatus = 1;

            try
            {
                videoId = null;
                Video newVideo = new Video();
                
                newVideo.Title = title;
                newVideo.Tags.Add(new MediaCategory("Nonprofit", YouTubeNameTable.CategorySchema));
                newVideo.Keywords = keywords;
                newVideo.Description = description;
                newVideo.YouTubeEntry.Private = true;
                
                newVideo.YouTubeEntry.setYouTubeExtension("location", location);
                newVideo.YouTubeEntry.MediaSource = new MediaFileSource(videoFileLocation, "video/wmv");

                Video createdVideo = request.Upload(newVideo);
                //Console.WriteLine(createdVideo.Id + " \t" + createdVideo.VideoId);

                videoId = createdVideo.VideoId;
                latestVideo = createdVideo;
                videoUploadStatus = 2;
                //Console.WriteLine("Status:"+createdVideo.Status.Name);
                //displayUserUploadedVideoList();

                EventLog.WriteEntry(sSource, "Video successfully uploaded on YouTube", EventLogEntryType.Information);

            }
            // catch all possible specialized exceptions and associate an error code with each of the following.

            // System.Net.WebException occurs when Internet Connection is not availaible.
            catch (System.Net.WebException exception)
            {
                // write the error messgae in the log file
                EventLog.WriteEntry(sSource, "Video Upload Failure:"+"No Internet Connection?:" + exception.Message
                    , EventLogEntryType.Error);
            }
            
            // System.IO.IOException could occur when the internet connection is terminated while
            // a video was uploading
            catch (System.IO.IOException exception)
            {
                // write the error messgae in the log file
                EventLog.WriteEntry(sSource, "Video Upload Failure:" + "Internet Connection Lost:" + exception.Message
                    , EventLogEntryType.Error);
            }
            
            // InvalidCredentialsException occurs when the youtube credentials are invalid
            catch (InvalidCredentialsException exception)
            {
                // write the error messgae in the log file
                EventLog.WriteEntry(sSource, "Video Upload Failure:" + "YouTube Credentials?:" + exception.Message
                                    , EventLogEntryType.Error);
            }
           
            // Catching the generic exceptions
            catch (Exception exception)
            {
                // videoUploadStaus=3 implies that an error occured while uploading the video
                videoUploadStatus = 3;
                // write the error Message in the log file rather than on console.
                EventLog.WriteEntry(sSource, "Video Upload Failure:" + exception.Message, EventLogEntryType.Error);
            }
           
            return videoUploadStatus;

        }


        public Hashtable getVideoIdModerationStatusVideoStatusFBStatus(String username, String branchId)
        {
            Hashtable videoRecord = new Hashtable();

            int startIndex = 1;
          
            while (true)
            {
                int temporaryCount = 0;

                Uri videoEntryUrl = new Uri("http://gdata.youtube.com/feeds/api/users/" + username + "/uploads?start-index=" + startIndex + "&max-results=50&v=1");

                //If internet connection is not there then the Get request will throw an exception.
                //If credentials are incorrect then invalid credentials exception will be thrown
                //If URL is incorrect then an Exception is thrown.

                try
                {
                    Feed<Video> videoFeed = request.Get<Video>(videoEntryUrl);

                    // here we assume that branch id is the first keyword for a video
                    foreach (Video video in videoFeed.Entries)
                    {
                        // code to identify the branch which uploaded the video

                        temporaryCount++;

                        try
                        {
                            String keywords = video.Keywords;
                            String[] individualkeywords = keywords.Split(',');

                            String branchInUploadedVideo = individualkeywords[1].Trim();


                            if (branchId.Equals(branchInUploadedVideo, StringComparison.OrdinalIgnoreCase))
                            {
                                bool uploadOnFacebook = false;
                                String facebookuploadString = individualkeywords[individualkeywords.Length - 1];
                                // Trim the string to remove any white space
                                facebookuploadString = facebookuploadString.Trim();
                                if (facebookuploadString.Equals("facebook", StringComparison.OrdinalIgnoreCase))
                                    uploadOnFacebook = true;

                                String videoStatus = "NA";
                                if (video.Status != null)
                                    videoStatus = video.Status.Name;

                                Tuple<Boolean, String, Boolean> tuple = new Tuple<bool, string, bool>
                                (video.Private, videoStatus, uploadOnFacebook);

                                videoRecord.Add(video.VideoId, tuple);
                            }
                        }
                        // This error would be thrown when keyword list has only one or zero keywords
                        catch (Exception generalException)
                        {
                            // log this event in the event logger
                            EventLog.WriteEntry(sSource, "Keywords in video are less than required:" + generalException.Message,
                                EventLogEntryType.Error);
                            // If there is problem with how keywords are defined in the present video 
                            //then move to the next video in the list

                            continue;

                        }

                    }

                    if (videoFeed.Entries.Count() != 50)
                        break;
                }
                catch (Exception generalException)
                {
                    // write the error Message in the log file rather than on console.
                    EventLog.WriteEntry(sSource, "Error in fetching information, may be because Internet is down:" + generalException.Message, EventLogEntryType.Error);
                    
                }
                
                if(temporaryCount!=0)
                    startIndex += 50;
            }

            return videoRecord;

        }

        // this function returns the hashtable consisting of VideoId, Moderation Status, Audio Id and Branch name
        public List<Tuple<String, Boolean, String, String, String>> getVideoDataofOtherBranches(String username, String branchId)
        {
            List<Tuple<String, Boolean, String, String, String>> videoRecord = new List<Tuple<String, Boolean, String, String, String>>();

            int startIndex = 1;
            
            while (true)
            {
                int temporaryCount = 0;
                Uri videoEntryUrl = new Uri("http://gdata.youtube.com/feeds/api/users/" + username + "/uploads?start-index=" + startIndex + "&max-results=50&v=1");

                //If internet connection is not there then the Get request will throw an exception.
                //If credentials are incorrect then invalid credentials exception will be thrown
                //If URL is incorrect then an Exception is thrown.

                try
                {
                    Feed<Video> videoFeed = request.Get<Video>(videoEntryUrl);

                    // here we assume that branch id is the first keyword for a video
                    foreach (Video video in videoFeed.Entries)
                    {
                        // code to identify the branch which uploaded the video

                        temporaryCount++;

                        String keywords = video.Keywords;
                        String[] individualKeywords = keywords.Split(',');

                        // It might be possible that video uploaded on YouTube doe not have keywords in correct format
                        // it might be possible that there are no keywords or less than desired
                        // the try catch block is used to ignore such videos.
                        try
                        {
                            String audioFileName = individualKeywords[0];
                            // Remove all leading and trailing white spaces from the code
                            String branchInUploadedVideo = individualKeywords[1].Trim();
                            String phoneNumberInUploadedVideo = individualKeywords[2].Trim();

                            if (!branchId.Equals(branchInUploadedVideo, StringComparison.OrdinalIgnoreCase))
                            {
                                Tuple<String, Boolean, String, String, String> tuple =
                                    new Tuple<String, Boolean, String, String, String>(video.VideoId, video.Private, audioFileName,
                                        branchInUploadedVideo, phoneNumberInUploadedVideo);
                                videoRecord.Add(tuple);
                            }
                        }
                        catch (Exception generalException)
                        {
                            // log this event in the event logger
                            EventLog.WriteEntry(sSource, "Keywords in video are less than required:" + generalException.Message,
                                EventLogEntryType.Error);
                            continue;
                        }
                    }

                    if (videoFeed.Entries.Count() < 50)
                        break;
                }
                catch (Exception generalException)
                {
                    // write the error Message in the log file rather than on console.
                    EventLog.WriteEntry(sSource, "Error fetching Video Data of other branches, because Internet is down:" + generalException.Message,
                        EventLogEntryType.Error);
                }

                if(temporaryCount!=0)
                    startIndex += 50;
            }

            return videoRecord;

        }

        public void displayUserUploadedVideoList()
        {

            Uri videoEntryUrl = new Uri("http://gdata.youtube.com/feeds/api/users/"+"testcgnetswara"+"/uploads");

                
                    //If internet connection is not there then the Get request will throw an exception.
                    //If credentials are incorrect then invalid credentials exception will be thrown
                    //If URL is incorrect then an Exception is thrown.

            try
            {
                Feed<Video> videoFeed = request.Get<Video>(videoEntryUrl);
                printVideoFeed(videoFeed);
            }
            catch (Exception errorMessage)
            {
                // write the error Message in the log file rather than on console.
                EventLog.WriteEntry(sSource, "Error in displaying user uploaded video list:" + errorMessage.Message,
                    EventLogEntryType.Error); 
            }
               
        }
        
        public void printVideoFeed(Feed<Video> feed)
        {
            foreach (Video entry in feed.Entries)
            {
                printVideoEntry(entry);
            }
        }
        public void printVideoEntry(Video video)
        {
            Console.WriteLine(video.WatchPage);
            Console.WriteLine("Author:" + video.Author);
            Console.WriteLine("Private?:" + video.Private);


            Console.WriteLine("Title: " + video.Title);
            Console.WriteLine(video.Description);
            Console.WriteLine("Keywords: " + video.Keywords);
            Console.WriteLine("Uploaded by: " + video.Uploader);
            if (video.YouTubeEntry.Location != null)
            {
                Console.WriteLine("Latitude: " + video.YouTubeEntry.Location.Latitude);
                Console.WriteLine("Longitude: " + video.YouTubeEntry.Location.Longitude);
            }
            if (video.Media != null && video.Media.Rating != null)
            {
                Console.WriteLine("Restricted in: " + video.Media.Rating.Country);
            }

            if (video.IsDraft)
            {
                Console.WriteLine("Video is not live.");
                string stateName = video.Status.Name;
                if (stateName == "processing")
                {
                    Console.WriteLine("Video is still being processed.");
                }
                else if (stateName == "rejected")
                {
                    Console.Write("Video has been rejected because: ");
                    Console.WriteLine(video.Status.Value);
                    Console.Write("For help visit: ");
                    Console.WriteLine(video.Status.Help);
                }
                else if (stateName == "failed")
                {
                    Console.Write("Video failed uploading because:");
                    Console.WriteLine(video.Status.Value);

                    Console.Write("For help visit: ");
                    Console.WriteLine(video.Status.Help);
                }

                if (video.ReadOnly == false)
                {
                    Console.WriteLine("Video is editable by the current user.");
                }

                if (video.RatingAverage != -1)
                {
                    Console.WriteLine("Average rating: " + video.RatingAverage);
                }
                if (video.ViewCount != -1)
                {
                    Console.WriteLine("View count: " + video.ViewCount);
                }

                Console.WriteLine("Thumbnails:");
                foreach (MediaThumbnail thumbnail in video.Thumbnails)
                {
                    Console.WriteLine("\tThumbnail URL: " + thumbnail.Url);
                    Console.WriteLine("\tThumbnail time index: " + thumbnail.Time);
                }

                Console.WriteLine("Media:");
                foreach (Google.GData.YouTube.MediaContent mediaContent in video.Contents)
                {
                    Console.WriteLine("\tMedia Location: " + mediaContent.Url);
                    Console.WriteLine("\tMedia Type: " + mediaContent.Format);
                    Console.WriteLine("\tDuration: " + mediaContent.Duration);
                }
            }
            
        }

        public Boolean deleteVideoFromYouTube(Object thisVideo)
        {

            //this.VideoId = YouTubeVideoID;
             // delete the last update video 
             // this is not a correct way of deleting a video
            bool isDeleteionSuccessful = false;
            Video videoToBeDeleted = (Video)thisVideo;
            try
            {
                request.Delete(videoToBeDeleted);
                isDeleteionSuccessful = true;
            }
            catch (Exception generalException)
            {
                // log this event in the event logger
                EventLog.WriteEntry(sSource, "Error deleting the video:" + generalException.Message, 
                    EventLogEntryType.Error);

            }

            return isDeleteionSuccessful;
        }

        public Boolean deleteVideoFromYouTube()
        {

            //this.VideoId = YouTubeVideoID;
            // delete the last update video 
            // this is not a correct way of deleting a video
            bool isDeleteionSuccessful = false;
            try
            {
                request.Delete(latestVideo);
                isDeleteionSuccessful = true;
            }
            catch (Exception generalException)
            {
                // log this event in the event logger
                EventLog.WriteEntry(sSource, "error deleting the video:" + generalException.Message, 
                    EventLogEntryType.Error);

            }

            return isDeleteionSuccessful;
        }

        // this function delete a video corresponding to a video id from YouTube Channel of NGO
        // this function is NOT TESTED YET
        public Boolean deleteVideoFromYouTube(String videoId)
        {
            bool isDeleted = false;

            try
            {
                Uri videoEntryUrl = new Uri("http://gdata.youtube.com/feeds/api/users/default/uploads/"+ videoId);
                Video video = request.Retrieve<Video>(videoEntryUrl);
                request.Delete(video);
                isDeleted = true;
                
            }
            catch (Exception generalException)
            {
                // log this event in the logger
                EventLog.WriteEntry(sSource, "Error deleting the video:" + generalException.Message, 
                    EventLogEntryType.Error);
            }

            return isDeleted;

        }

        public Tuple<String, String, Uri> getVideoInformation(String videoId)
        {

            Tuple<String, String, Uri> videoInformation = null;
            try
            {
                Uri videoEntryUrl = new Uri("http://gdata.youtube.com/feeds/api/users/default/uploads/" + videoId);
                Video video = request.Retrieve<Video>(videoEntryUrl);
                //Google.GData.YouTube.MediaContent mediaContent = (Google.GData.YouTube.MediaContent)video.Contents;
                videoInformation = Tuple.Create(video.Title, video.Description, video.WatchPage); 


            }
            catch (Exception genericException)
            {
                // log this event in the logger
                EventLog.WriteEntry(sSource, "Error getting video information:" + genericException.Message, 
                    EventLogEntryType.Error);
                
            }

            return videoInformation;

        }



        public Boolean testYouTubeSettings(String Username)
        {
            bool isYouTubeSettingsCorrect = false;
            Uri videoEntryUrl = new Uri("http://gdata.youtube.com/feeds/api/users/" + Username + "/uploads");


            //If internet connection is not there then the Get request will throw an exception.
            //If credentials are incorrect then invalid credentials exception will be thrown
            //If URL is incorrect then an Exception is thrown.

            try
            {
                 request.Get<Video>(videoEntryUrl);
                 isYouTubeSettingsCorrect = true;

            }
            catch (Exception errorMessage)
            {
                // write the error Message in the log file rather than on console.
                EventLog.WriteEntry(sSource, "Error encountered while testing YouTube settings:" + errorMessage.Message, 
                    EventLogEntryType.Error);
            } 
            
            return isYouTubeSettingsCorrect;
        }
     
    }
}
